// arch-tag: 045c160d-e4f8-4390-90de-30bcdc258f91
package de.yvert.jingle.help;

import java.io.DataInput;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.InputStream;
import java.io.IOException;
import java.nio.ByteOrder;

/**
 * An <code>InputStream</code> that can change the byte order. Kind of like
 * <code>InputStream</code> crossed with <code>DataInput</code> and
 * <code>java.nio.ByteOrder</code>.
 * <p>
 * TODO: Kick the &#(*&!^ <code>java.nio</code>-Programmers.
 * 
 * @author Ulf Ochsenfahrt
 */
public final class ConverterInputStream extends FilterInputStream implements DataInput
{

public static final ByteOrder BIG_ENDIAN = ByteOrder.BIG_ENDIAN;
public static final ByteOrder LITTLE_ENDIAN = ByteOrder.LITTLE_ENDIAN;

protected ByteOrder order;

public ConverterInputStream(ByteOrder order, InputStream in)
{
	super(in);
	this.order = order;
}

public ConverterInputStream(InputStream in)
{ this(BIG_ENDIAN, in); }

public void setByteOrder(ByteOrder order)
{ this.order = order; }

public ByteOrder getByteOrder()
{ return order; }

private int secureRead() throws IOException
{
	int value = read();
	if (value < 0) throw new EOFException();
	return value;
}

public boolean readBoolean() throws IOException
{ return secureRead() != 0; }

public byte readByte() throws IOException
{ return (byte) secureRead(); }

public char readChar() throws IOException
{ return (char) readUnsignedShort(); }

public double readDouble() throws IOException
{ return Double.longBitsToDouble(readLong()); }

public float readFloat() throws IOException
{ return Float.intBitsToFloat(readInt()); }

public void readFully(byte[] b) throws IOException
{ readFully(b, 0, b.length); }

public void readFully(byte[] b, int off, int len) throws IOException
{
	int count = 0;
	while (count < len)
	{
		int temp = in.read(b, off+count, len-count);
		if (temp <= 0) throw new EOFException();
		count += temp;
	}
}

public short readShort() throws IOException
{ return (short) readUnsignedShort(); }

public int readInt() throws IOException
{ return (int) readUnsignedInt(); }

public long readLong() throws IOException
{
	long a = readUnsignedInt();
	long b = readUnsignedInt();
	if (order == BIG_ENDIAN)
		return (a << 32) | b;
	else
		return (b << 32) | a;
}

public int readUnsignedByte() throws IOException
{ return secureRead(); }

public int readUnsignedShort() throws IOException
{
	int a = secureRead();
	int b = secureRead();
	if (order == BIG_ENDIAN)
		return (a << 8) | b;
	else
		return (b << 8) | a;
}

public long readUnsignedInt() throws IOException
{
	long a = secureRead();
	long b = secureRead();
	long c = secureRead();
	long d = secureRead();
	if (order == BIG_ENDIAN)
		return (a << 24) | (b << 16) | (c << 8) | d;
	else
		return (d << 24) | (c << 16) | (b << 8) | a;
}

@SuppressWarnings("unused")
public String readLine() throws IOException
{ throw new RuntimeException("Not implemented!"); }

@SuppressWarnings("unused")
public String readUTF() throws IOException
{ throw new RuntimeException("Not implemented!"); }

public int skipBytes(int n) throws IOException
{ return (int) skip(n); }

public void seek(int position) throws IOException
{
	if (in instanceof SeekableInputStream)
		((SeekableInputStream) in).seek(position);
	else
		throw new IOException("In-Stream not seekable!");
}

}
